﻿namespace MockingBird.Common.Infrastructure.Configuration
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Globalization;
    using System.IO;
    using System.Xml;
    /// <summary>
    /// Utility class to help with handling configuration data, config files etc.
    /// </summary>
    public class ConfigurationUtils
    {
        /// <summary>
        /// Gets the file path for a sibling configuration file in the application root.
        /// For example, alongside web.config we would place unity.config and log4net.config
        /// and this would get us the full path to that file.
        /// </summary>
        /// <param name="siblingConfigFileName">
        /// Name of the sibling for which we want the path.
        /// </param>
        /// <returns>
        /// The get sibling config file path.
        /// </returns>
        public static string GetSiblingConfigFilePath(string siblingConfigFileName)
        {
            string applicationBasePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            return String.Format(CultureInfo.CurrentCulture, @"{0}\{1}", applicationBasePath, siblingConfigFileName);

        }

        /// <summary>
        /// Returns the folder where sibling config files should be placed
        /// </summary>
        /// <returns></returns>
        public static string GetSiblingConfigFolder()
        {
            return AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
        }

        /// <summary>
        /// Parses the mockingbird.config file and returns a configuration section that matches the section NAME.
        /// Looks into the mockingbird.config file name 
        /// </summary>
        /// <param name="sectionName">Name of the required section.</param>
        /// <returns></returns>
        public static object GetConfigSection(string sectionName)
        {
            string configFileName = ConfigurationUtils.GetSiblingConfigFilePath("mockingbird.config");
            ValidateConfigFileExistence(configFileName);

            // pass to the overloaded method
            return GetConfigSection(configFileName, sectionName);
        }

        /// <summary>
        /// Gets the raw section xml corresponding to the section name
        /// </summary>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public static string GetConfigSectionXml(string sectionName)
        {
            ConfigurationSection targetSection = (ConfigurationSection)GetConfigSection(sectionName);
            if (targetSection == null)
            {
                throw new ConfigurationErrorsException(
                    String.Format(
                        "The specified config file did not have any section named {0}",
                        sectionName));
            }

            return targetSection.SectionInformation.GetRawXml();
        }



        public static object GetConfigSection(string configFileName, string sectionName)
        {
            ValidateConfigFileExistence(configFileName);
            ExeConfigurationFileMap map = new ExeConfigurationFileMap();
            map.ExeConfigFilename = configFileName;
            System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

            if (config == null)
            {
                throw new ConfigurationErrorsException(
                    String.Format(
                        "The specified config file {0} did not have any section named {1}",
                        configFileName,
                        sectionName));
            }

            ConfigurationManager.RefreshSection(sectionName);
            return config.GetSection(sectionName);
        }


        /// <summary>
        /// gets the raw section xml corresponding to the section name in the specified config file.
        /// </summary>
        /// <param name="configFileName"></param>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public static string GetConfigSectionXml(string configFileName, string sectionName)
        {
            ConfigurationSection targetSection = (ConfigurationSection)ConfigurationUtils.GetConfigSection(configFileName, sectionName);

            return targetSection.SectionInformation.GetRawXml();
        }

        private static void ValidateConfigFileExistence(string configFileName)
        {
            if (!File.Exists(configFileName))
            {
                throw new FileNotFoundException("Could not locate config file. Looking for:" + configFileName);
            }


        }



        public static Dictionary<string, string> LoadDictionaryFromNameValueSettings(XmlDocument sectionDoc, string enclosingNodeName, string keyNodeName, string valueNodeName)
        {
            Dictionary<string, string> settingsDictionary = new Dictionary<string, string>();
            XmlNodeList nodeList = sectionDoc.SelectNodes("//" + enclosingNodeName);
            if (nodeList != null)
            {
                foreach (XmlNode settingNode in nodeList)
                {
                    string settingName = settingNode.Attributes[keyNodeName].InnerText;
                    string settingValue = settingNode.Attributes[valueNodeName].InnerText;

                    if (settingValue.Contains("$"))
                    {
                        settingValue = ConfigurationUtils.Detokenize(settingValue, settingsDictionary);
                    }

                    settingsDictionary.Add(settingName, settingValue);
                }
            }
            return settingsDictionary;
        }

        public static string Detokenize(string tokenizedString, Dictionary<string, string> settingsDictionary)
        {
            // look for the token in the input string 
            // This may run into trouble with advanced tokens but should do for now
            int tokenStartPos = tokenizedString.IndexOf("$(");
            int tokenEndPos = tokenizedString.IndexOf(")");
            string tokenKey = tokenizedString.Substring(tokenStartPos + 2, tokenEndPos - 2);

            // now reconstitute the token so it can be substituted
            string token = "$(" + tokenKey + ")";

            string dictionaryValue = String.Empty;
            bool lookupResult = settingsDictionary.TryGetValue(tokenKey, out dictionaryValue);

            if (!lookupResult)
            {
                throw new ApplicationException("Error in detokenizing configuration setting : " +
                                                       tokenizedString);
            }

            string newValue = tokenizedString.Replace(token, dictionaryValue);

            return newValue;

        }

    }
}
